/* * Copyright (C) 2011 aki@akjava.com * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.akjava.gwt.subplayer.client; import java.io.IOException; import java.util.ArrayList; import java.util.List; import com.akjava.gwt.chrometts.client.ChromeTts; import com.akjava.gwt.chrometts.client.TtsEvent; import com.akjava.gwt.chrometts.client.TtsOption; import com.akjava.gwt.chrometts.client.TtsVoice; import com.akjava.gwt.chrometts.client.ChromeTts.GetVoiceHandler; import com.akjava.gwt.chrometts.client.ChromeTts.TtsEventHandler; import com.akjava.gwt.subplayer.client.resources.Binder; import com.akjava.gwt.subplayer.client.ui.HTML5InputRange; import com.akjava.subtitle.client.srt.SRTList; import com.akjava.subtitle.client.srt.SRTObject; import com.akjava.subtitle.client.srt.SRTParser; import com.google.gwt.core.client.EntryPoint; import com.google.gwt.core.client.JsArray; import com.google.gwt.dom.client.Style.Unit; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.event.dom.client.LoadEvent; import com.google.gwt.event.dom.client.LoadHandler; import com.google.gwt.event.dom.client.MouseUpEvent; import com.google.gwt.event.dom.client.MouseUpHandler; import com.google.gwt.event.logical.shared.ValueChangeEvent; import com.google.gwt.event.logical.shared.ValueChangeHandler; import com.google.gwt.resources.client.ImageResource; import com.google.gwt.text.shared.Renderer; import com.google.gwt.user.client.Timer; import com.google.gwt.user.client.ui.Button; import com.google.gwt.user.client.ui.DialogBox; import com.google.gwt.user.client.ui.DisclosurePanel; import com.google.gwt.user.client.ui.DockLayoutPanel; import com.google.gwt.user.client.ui.FocusPanel; import com.google.gwt.user.client.ui.HTMLPanel; import com.google.gwt.user.client.ui.HorizontalPanel; import com.google.gwt.user.client.ui.Image; import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.RootLayoutPanel; import com.google.gwt.user.client.ui.RootPanel; import com.google.gwt.user.client.ui.ScrollPanel; import com.google.gwt.user.client.ui.TabLayoutPanel; import com.google.gwt.user.client.ui.TextArea; import com.google.gwt.user.client.ui.ValueListBox; import com.google.gwt.user.client.ui.VerticalPanel; import com.google.gwt.user.client.ui.Widget; public class SubPlayer implements EntryPoint,SubContainer { private VerticalPanel itemPanel; private LoadPanel loadPanel; @Override public void onModuleLoad() { //pre load resource ImageResource icon=Binder.INSTANCE.loadanime(); loadImg = new Image(icon); loadImg.setVisible(false); loadImg.addLoadHandler(new LoadHandler() { @Override public void onLoad(LoadEvent event) { RootPanel.get().remove(loadImg); loadImg.setVisible(true); } }); RootPanel.get().add(loadImg); preference = new SubPlayerPreference(); preference.initialize(); tab = new TabLayoutPanel(2, Unit.EM); tab.setHeight("500px"); VerticalPanel root=new VerticalPanel(); root.setWidth("100%"); root.setHeight("100%"); //root.setHeight("200px"); DockLayoutPanel doc=new DockLayoutPanel(Unit.PX); doc.addSouth(new HTMLPanel("<div align='center'>Subtitle TTS Player by <a href='http://www.akjava.com'>akjava.com</a></div>"), 40); doc.add(tab); RootLayoutPanel.get().add(doc); //RootLayoutPanel.get().add(new Label("hello")); tab.add(root, "PLAY"); noSubtitle = new Label("Subtitle is empty.load from Load tab"); noSubtitle.setStyleName("nosubtitle"); root.add(noSubtitle); loadPanel = new LoadPanel(); loadPanel.setWidth("100%"); loadPanel.setHeight("100%"); tab.add(loadPanel, "LOAD"); loadPanel.setText(preference.getSrtText()); itemPanel = new VerticalPanel(); itemPanel.setSpacing(8); itemPanelScroll = new ScrollPanel(itemPanel); itemPanelScroll.setWidth("100%"); itemPanelScroll.setHeight("350px"); root.add(itemPanelScroll); /* for(int i=0;i<5;i++){ String text=i+" hello world\n"; for(int j=0;j<i;j++){ text+="line\n"; } HTMLPanel label=new HTMLPanel(text.replace("\n", "<br/>")); FocusPanel panel=new FocusPanel(label); panel.addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { unselectAll(); setlectWidget((Widget) event.getSource()); } }); //label.setHeight("100px"); itemPanel.add(panel); }*/ playerWidget = new PlayerWidget(this); root.add(playerWidget); DisclosurePanel ds=new DisclosurePanel("show subtitle time [start] - [end]"); timeLabel = new Label(); ds.add(timeLabel); //ds.add(new Label("0:0:0 - 0:0:12")); root.add(ds); selectWidgetHandler=new ClickHandler() { @Override public void onClick(ClickEvent event) { unselectAll(); setlectWidget((Widget) event.getSource()); } }; DisclosurePanel vs=new DisclosurePanel("Voice Settings"); root.add(vs); voiceSettings = new VoiceSettings(); vs.add(voiceSettings); //load data from preferences //if empty load mode. if(!loadPanel.getText().isEmpty()){ loadSrt(preference.getSrtSelectIndex()); }else{ tab.selectTab(1); } } public class SRTItemPanel extends FocusPanel{ SRTObject srt; public SRTObject getSrt() { return srt; } public SRTItemPanel(SRTObject srt){ this.srt=srt; HorizontalPanel hpanel=new HorizontalPanel(); hpanel.setSpacing(2); hpanel.setVerticalAlignment(HorizontalPanel.ALIGN_MIDDLE); Button play=new Button("PLAY"); play.addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { int index=itemPanel.getWidgetIndex(SRTItemPanel.this); play(index); } }); hpanel.add(play); HTMLPanel html=new HTMLPanel(srt.getText().replace("\n", "<br/>")); hpanel.add(html); setWidget(hpanel); } } ClickHandler selectWidgetHandler; private TabLayoutPanel tab; private Label noSubtitle; private SubPlayerPreference preference; private Image loadImg; private PlayerWidget playerWidget; private ScrollPanel itemPanelScroll; private Label timeLabel; public class LoadPanel extends VerticalPanel{ private TextArea textArea; public LoadPanel(){ Label label=new Label("To load srt,copy and paste srt text from srt file and click load button."); add(label); setSpacing(4); textArea = new TextArea(); textArea.setWidth("100%"); textArea.setHeight("350px"); add(textArea); HorizontalPanel bcontrol=new HorizontalPanel(); add(bcontrol); Button load=new Button("LOAD"); load.addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { loadSrt(0); } }); bcontrol.add(load); Button clear=new Button("CLEAR"); clear.addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { clearSrt(); } }); bcontrol.add(clear); } public String getText(){ return textArea.getText(); } public void setText(String text){ textArea.setText(text); } } private void clearSrt() { loadPanel.setText(""); itemPanel.clear(); initPlayerSettings(); preference.setSrtText(""); updateNoSubtitleWarning(); } private void updateNoSubtitleWarning(){ if(itemPanel.getWidgetCount()>0){ noSubtitle.setVisible(false); }else{ noSubtitle.setVisible(true); } } private void loadSrt(final int selectIndex) { final String text=loadPanel.getText(); final DialogBox dialog=new DialogBox(); //dialog.setSize("200px", "200px"); dialog.setText("Subtitle Parsing"); //GWT.log(loadImg.getUrl()); //loadImg.setVisible(true); VerticalPanel vpanel=new VerticalPanel(); //Image img=new Image("../img/loadanime.gif"); //GWT.log(img.getUrl()); //loadImg.setVisible(true); vpanel.add(loadImg); dialog.setWidget(vpanel); dialog.setModal(true); dialog.setGlassEnabled(true); dialog.show(); dialog.center(); Timer timer=new Timer(){ @Override public void run() { SRTParser parser=new SRTParser(); SRTList list=parser.parse(text.split("\n")); dialog.hide(); playerWidget.setSubLength(list.size()); if(list.size()>0){ preference.setSrtText(text); preference.setSrtSelectIndex(0); playerWidget.setSubIndex(0); } itemPanel.clear(); for(int i=0;i<list.size();i++){ SRTObject srt=list.getSRTObjectAt(i); SRTItemPanel panel=new SRTItemPanel(srt); panel.addClickHandler(selectWidgetHandler); itemPanel.add(panel); } initPlayerSettings(); tab.selectTab(0);//switch to view //label mode updateNoSubtitleWarning(); selectWidget(selectIndex); }}; timer.schedule(100); } private void initPlayerSettings(){ playerWidget.setSubIndex(0); } private void selectWidget(int index){ if(index<itemPanel.getWidgetCount()){ Widget widget=itemPanel.getWidget(index); setlectWidget(widget); } } private SRTItemPanel getSelectWidget(){ return (SRTItemPanel) itemPanel.getWidget(playerWidget.getSubIndex()); } private SRTItemPanel getWidget(int index){ return (SRTItemPanel) itemPanel.getWidget(index); } private void setlectWidget(Widget widget){ widget.addStyleName("select"); int ind=itemPanel.getWidgetIndex(widget); playerWidget.setSubIndex(ind); SRTItemPanel srtItem=(SRTItemPanel) widget; SRTObject srt=srtItem.getSrt(); timeLabel.setText(srt.getStartTime().toString()+" - "+srt.getEndTime().toString()); } private void unselectAll(){ for(int i=0;i<itemPanel.getWidgetCount();i++){ itemPanel.getWidget(i).removeStyleName("select"); } } private int calculateScrollY(int index){ int scroll=0; for(int i=0;i<index;i++){ scroll+=itemPanel.getWidget(i).getOffsetHeight(); scroll+=itemPanel.getSpacing(); } return scroll; } private Timer tmpTimer; private VoiceSettings voiceSettings; @Override public void autoPlay(int index) { play(index); } private void endPlay(){ if(playerWidget.hasNext()){ playerWidget.doNext(); play(playerWidget.getSubIndex()); }else{ playerWidget.endAutoPlay(); } } @Override public void moveTo(int index) { int sat=calculateScrollY(index); itemPanelScroll.setVerticalScrollPosition(sat); unselectAll(); setlectWidget(itemPanel.getWidget(index)); } TtsEventHandler eventHandler; @Override public void play(int index) { // TODO call tts if(eventHandler==null){ eventHandler=new TtsEventHandler() { @Override public void event(TtsEvent event) { if(event.getType().equals(TtsEvent.TYPE_END) || event.getType().equals(TtsEvent.TYPE_CANCELLED) || event.getType().equals(TtsEvent.TYPE_INTERRUPTED) || event.getType().equals(TtsEvent.TYPE_ERROR) ){ // endPlay(); } } }; }; if(ChromeTts.isAvaialbe()){ String text=getWidget(index).getSrt().getText(); TtsOption option=ChromeTts.options().rate(voiceSettings.getRate()).pitch(voiceSettings.getPitch()).voiceName(voiceSettings.getVoiceName()); if(playerWidget.isAutoPlaying()){ option.onEvent(eventHandler); } ChromeTts.speak(text, option, null); } } @Override public void stop() { playerWidget.setAutoPlaying(false); ChromeTts.stop(); } public class VoiceSettings extends VerticalPanel{ Label rateValue; private Label pitchValue; private HTML5InputRange pitchRange; private HTML5InputRange rateRange; private ValueListBox<String> voiceNames; public VoiceSettings(){ voiceNames = new ValueListBox<String>(new Renderer<String>() { @Override public String render(String object) { return object; } @Override public void render(String object, Appendable appendable) throws IOException { } }); voiceNames.addValueChangeHandler(new ValueChangeHandler<String>() { @Override public void onValueChange(ValueChangeEvent<String> event) { String value=event.getValue(); preference.setVoiceName(value); } }); add(voiceNames); updateVoiceNames(); HorizontalPanel voices=new HorizontalPanel(); add(voices); voices.add(new Label("Voices:")); voices.add(voiceNames); Button reset=new Button("Reset"); voices.add(reset); reset.addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { preference.setVoiceName(""); preference.setVoiceRate(1); preference.setVoicePitch(1); updateVoiceNames(); rateRange.setValue(10); rateValue.setText("RATE:"+1); pitchRange.setValue(10); pitchValue.setText("PITCH:"+1); } }); HorizontalPanel settings=new HorizontalPanel(); add(settings); rateValue=new Label(); rateValue.setWidth("60px"); settings.add(rateValue); int drv=(int) (preference.getVoiceRate()*10); rateValue.setText("RATE:"+drv); rateRange = new HTML5InputRange(1, 50, drv); rateRange.setWidth("100px"); rateRange.addMouseUpHandler(new MouseUpHandler() { @Override public void onMouseUp(MouseUpEvent event) { double rv=(double)rateRange.getValue()/10; rateValue.setText("RATE:"+rv); preference.setVoiceRate(rv); } }); settings.add(rateRange); pitchValue=new Label(); pitchValue.setWidth("60px"); settings.add(pitchValue); int dpv=(int) (preference.getVoicePitch()*10); pitchValue.setText("PITCH:"+dpv); pitchRange = new HTML5InputRange(1, 20, dpv); pitchRange.setWidth("100px"); pitchRange.addMouseUpHandler(new MouseUpHandler() { @Override public void onMouseUp(MouseUpEvent event) { double pv=(double)pitchRange.getValue()/10; pitchValue.setText("PITCH:"+pv); preference.setVoicePitch(pv); } }); settings.add(pitchRange); } public String getVoiceName(){ return voiceNames.getValue(); } private void updateVoiceNames(){ if(ChromeTts.isAvaialbe()){ ChromeTts.getVoices(new GetVoiceHandler() { @Override public void voices(JsArray<TtsVoice> voices) { String dv=preference.getVoiceName(); List<String> names=new ArrayList<String>(); for(int i=0;i<voices.length();i++){ names.add(voices.get(i).getVoiceName()); } String vname=names.get(0); if(names.contains(dv)){ vname=dv; } voiceNames.setValue(vname); voiceNames.setAcceptableValues(names); } }); } } public double getPitch(){ return (double)pitchRange.getValue()/10; } public double getRate(){ return (double)rateRange.getValue()/10; } } }